﻿using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace DevGenerators;

[Generator(LanguageNames.CSharp)]
public class CompilerDynamicDependenciesGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var invocationSyntaxes = context.SyntaxProvider
            .CreateSyntaxProvider(
                static (s, _) => s is InvocationExpressionSyntax inv && inv.Expression is MemberAccessExpressionSyntax
                {
                    Name: { Identifier: { Text: "GetType" or "FindType" } }
                } && inv.ArgumentList.Arguments.Count == 1 && inv.ArgumentList.Arguments[0].Expression is LiteralExpressionSyntax,
                static (context, _) => (InvocationExpressionSyntax)context.Node!);

        context.RegisterSourceOutput(invocationSyntaxes.Collect(), static (context, invocations) =>
        {
            var types = invocations
                .Select(t => t.ArgumentList.Arguments[0].GetText().ToString())
                .Distinct()
                .Select(t => t
                    .Replace("\"", "")
                    .Replace("`1", "<>")
                    .Replace("`2", "<,>")
                    .Replace("`3", "<,,>")
                    .Replace("`4", "<,,,>"))
                .OrderBy(t => t)
                .ToArray();
            if (types.Length == 0)
            {
                return;
            }

            var attributesBuilder = new StringBuilder();
            attributesBuilder.AppendLine("""
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by CompilerDynamicDependenciesGenerator source generator.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Diagnostics.CodeAnalysis;
#if XAML_RUNTIME_LOADER
namespace Avalonia.Markup.Xaml.XamlIl
{
    internal sealed partial class CompilerDynamicDependenciesAttribute
    {
""");
            foreach (var type in types)
            {
                if (type is "System.Void" or "XamlX.XamlDebugHatch"
                    || type.StartsWith("CompiledAvaloniaXaml"))
                {
                    continue;
                }

                attributesBuilder.AppendFormat(
"       [DynamicDependency(XamlDynamicallyAccessedMemberTypes, typeof({0}))]", type);
                attributesBuilder.AppendLine();
            }
            attributesBuilder.AppendLine("""
        public CompilerDynamicDependenciesAttribute() {}
    }
}
#endif
""");
            var str = attributesBuilder.ToString();
            context.AddSource("CompilerDynamicDependenciesAttribute.generated.cs", attributesBuilder.ToString());
        });
    }
}
